作者:张洪幸_246 | 来源:互联网 | 2023-06-26 10:50
篇首语:本文由编程笔记#小编为大家整理,主要介绍了性能优化之异步日志相关的知识,希望对你有一定的参考价值。
你听说过打印日志能把系统拖垮的情况吗。
所以项目中一般打印日志会使用异步AsyncAppender打印日志。
那么使用了AsyncAppender,会不会性能就好了,就不会阻塞业务流程了,会不会丢失日志呢,我们来看一下logback的实现。
先看下官方文档的介绍,AsyncAppender 会把处理的事件缓存到一个阻塞队列,默认情况下达到队列容量的80%的时候,会丢弃TRACE, DEBUG and INFO级别的事件,
根据默认配置neverBlock=false,队列也会发生阻塞,所以设置true,虽然完全是非阻塞但会丢失日志,特别是error级别的日志。
我们看下源码如何实现的。
public class AsyncAppender extends AsyncAppenderBase
boolean includeCallerData = false;
/**
* Events of level TRACE, DEBUG and INFO are deemed to be discardable.
* @param event
* @return true if the event is of level TRACE, DEBUG or INFO false otherwise.
*/
protected boolean isDiscardable(ILoggingEvent event)
Level level = event.getLevel();
return level.toInt() <&#61; Level.INFO_INT;
protected void preprocess(ILoggingEvent eventObject)
eventObject.prepareForDeferredProcessing();
if (includeCallerData)
eventObject.getCallerData();
public boolean isIncludeCallerData()
return includeCallerData;
public void setIncludeCallerData(boolean includeCallerData)
this.includeCallerData &#61; includeCallerData;
根据实现&#xff1a;
protected boolean isDiscardable(ILoggingEvent event)
Level level &#61; event.getLevel();
return level.toInt() <&#61; Level.INFO_INT;
public static final int OFF_INT &#61; Integer.MAX_VALUE;
public static final int ERROR_INT &#61; 40000;
public static final int WARN_INT &#61; 30000;
public static final int INFO_INT &#61; 20000;
public static final int DEBUG_INT &#61; 10000;
public static final int TRACE_INT &#61; 5000;
public static final int ALL_INT &#61; Integer.MIN_VALUE;
TRACE, DEBUG and INFO级别的事件&#xff0c;在容量默认到达80%的时候会丢弃。
事件放入阻塞队列的时候即调用put方法时会判断容量及是否可以丢弃条件的判断&#xff0c;如下&#xff1a;
&#64;Override
protected void append(E eventObject)
if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject))
return;
preprocess(eventObject);
put(eventObject);
private boolean isQueueBelowDiscardingThreshold()
return (blockingQueue.remainingCapacity()
private void put(E eventObject)
if (neverBlock)
blockingQueue.offer(eventObject);
else
putUninterruptibly(eventObject);
private void putUninterruptibly(E eventObject)
boolean interrupted &#61; false;
try
while (true)
try
blockingQueue.put(eventObject);
break;
catch (InterruptedException e)
interrupted &#61; true;
finally
if (interrupted)
Thread.currentThread().interrupt();
当事件真正放入队列&#xff0c;是调用阻塞方法put&#xff0c;还是非阻塞方法offer&#xff0c;是由neverBlock配置决定的&#xff0c;默认false&#xff0c;会阻塞&#xff0c;所以为了性能&#xff0c;我们可以设置为true&#xff0c;也会丢弃日志。
真正处理队列事件的线程是默无闻的后台&#xff08;Daemon&#xff09;消费线程&#xff0c;
Worker worker &#61; new Worker();
worker.setDaemon(true);
worker.setName("AsyncAppender-Worker-" &#43; getName());
// make sure this instance is marked as "started" before staring the worker Thread
super.start();
worker.start()
事件的最终归宿AppenderAttachableImpl aa。
class Worker extends Thread
public void run()
AsyncAppenderBase parent &#61; AsyncAppenderBase.this;
AppenderAttachableImpl aai &#61; parent.aai;
// loop while the parent is started
while (parent.isStarted())
try
E e &#61; parent.blockingQueue.take();
aai.appendLoopOnAppenders(e);
catch (InterruptedException ie)
break;
addInfo("Worker thread will flush remaining events before exiting. ");
for (E e : parent.blockingQueue)
aai.appendLoopOnAppenders(e);
parent.blockingQueue.remove(e);
aai.detachAndStopAllAppenders();
异步打印日志是否非阻塞&#xff0c;是否会丢弃日志&#xff0c;是完全可以配置的&#xff0c;如果可以丢弃&#xff0c;依据日志的报警系统就不太准了&#xff0c;只能靠打点系统在系统内部aop的方式或其他方式打点上报了。